// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

#ifndef RECOVERY_IMAGE_H_
#define RECOVERY_IMAGE_H_

#include <stdint.h>
#include <stddef.h>
#include "status/rot_status.h"
#include "common/signature_verification.h"
#include "crypto/hash.h"
#include "flash/flash.h"
#include "manifest/pfm/pfm_manager.h"
#include "flash/spi_flash.h"


/**
 * The API for interfacing with the recovery image.
 */
struct recovery_image {
	/**
	 * Verify if the recovery image is valid.
	 *
	 * @param image The recovery image to validate.
	 * @param hash The hash engine to use for validation.
	 * @param verification Verification instance to use to verify the recovery image signature.
	 * @param hash_out Optional output buffer for the recovery image hash calculated during
	 * verification.  Set to null to not return the hash.
	 * @param hash_length Length of the hash output buffer.
	 * @param pfm_manager The PFM manager to use for validation.
	 *
	 * @return 0 if the recovery image is valid or an error code.
	 */
	int (*verify) (struct recovery_image *image, struct hash_engine *hash,
		struct signature_verification *verification, uint8_t *hash_out, size_t hash_length,
		struct pfm_manager *pfm);

	/**
	 * Get the SHA-256 hash of the recovery image data, not including the signature.
	 *
	 * @param image The recovery image to query.
	 * @param hash The hash engine to use for generating the hash.
	 * @param hash_out Output buffer for the manifest hash.
	 * @param hash_length Length of the hash output buffer.
	 *
	 * @return 0 if the hash was calculated successfully or an error code.
	 */
	int (*get_hash) (struct recovery_image *image, struct hash_engine *hash, uint8_t *hash_out,
		size_t hash_length);

	/**
	 * Get the version of the recovery image.
	 *
	 * @param image The recovery image to query.
	 * @param version The buffer to hold the version ID.
	 * @param len The output buffer length.
	 *
	 * @return 0 if the ID was successfully retrieved or an error code.
	 */
	int (*get_version) (struct recovery_image *image, char *version, size_t len);

	/**
	 * Apply the recovery image to host flash.  It is assumed that the host flash region is already
	 * blank.
	 *
	 * @param image The recovery image to query.
	 * @param flash The flash device to write the recovery image to.
	 *
	 * @return 0 if applying the recovery image to host flash was successful or an error code.
	 */
	int (*apply_to_flash) (struct recovery_image *image, struct spi_flash *flash);

	struct flash *flash;							/**< The flash device that contains the recovery image. */
 	uint32_t addr;									/**< The starting address in flash of the recovery image. */
	uint8_t hash_cache[SHA256_HASH_LENGTH];			/**< Cache for the recovery image hash. */
	bool cache_valid;                       		/**< Flag indicating if the cached hash is valid. */
};

int recovery_image_init (struct recovery_image *image, struct flash *flash, uint32_t base_addr);
void recovery_image_release (struct recovery_image *image);


#define	RECOVERY_IMAGE_ERROR(code)		ROT_ERROR (ROT_MODULE_RECOVERY_IMAGE, code)

/**
 * Error codes that can be generated by a recovery image.
 */
enum {
	RECOVERY_IMAGE_INVALID_ARGUMENT = RECOVERY_IMAGE_ERROR (0x00),			/**< Input parameter is null or not valid. */
	RECOVERY_IMAGE_NO_MEMORY = RECOVERY_IMAGE_ERROR (0x01),					/**< Memory allocation failed. */
	RECOVERY_IMAGE_HASH_BUFFER_TOO_SMALL = RECOVERY_IMAGE_ERROR (0x02),		/**< A buffer for hash output was too small. */
	RECOVERY_IMAGE_VERIFY_FAILED = RECOVERY_IMAGE_ERROR (0x03),				/**< A verify failure unrelated to authentication. */
	RECOVERY_IMAGE_MALFORMED = RECOVERY_IMAGE_ERROR (0x04),					/**< The recovery image is not formated correctly. */
	RECOVERY_IMAGE_GET_HASH_FAILED = RECOVERY_IMAGE_ERROR (0x05),			/**< The hash could not be calculated. */
	RECOVERY_IMAGE_INCOMPATIBLE = RECOVERY_IMAGE_ERROR (0x06),				/**< The recovery image is incompatible with the system. */
	RECOVERY_IMAGE_INVALID_SECTION_ADDRESS = RECOVERY_IMAGE_ERROR (0x07),	/**< The section address is an invalid value. */
	RECOVERY_IMAGE_ID_BUFFER_TOO_SMALL = RECOVERY_IMAGE_ERROR (0x08),		/**< A buffer for version output was too small. */
};


#endif /* RECOVERY_IMAGE_H_ */
